// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.core.textures;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.function.Consumer;
import org.lwjgl.opengl.GL11;
import zombie.characterTextures.BloodBodyPartType;
import zombie.characterTextures.CharacterSmartTexture;
import zombie.core.Core;
import zombie.core.ImmutableColor;
import zombie.core.logger.ExceptionLogger;
import zombie.core.opengl.SmartShader;
import zombie.core.skinnedmodel.ModelManager;
import zombie.core.skinnedmodel.model.CharacterMask;
import zombie.core.utils.WrappedBuffer;
import zombie.debug.DebugLog;
import zombie.util.Lambda;
import zombie.util.list.PZArrayUtil;

public class SmartTexture extends Texture {
    public final ArrayList<TextureCombinerCommand> commands = new ArrayList();
    public Texture result;
    private boolean dirty = true;
    private static SmartShader hue;
    private static SmartShader tint;
    private static SmartShader masked;
    private static SmartShader dirtMask;
    private final HashMap<Integer, ArrayList<Integer>> categoryMap = new HashMap();
    private static SmartShader bodyMask;
    private static SmartShader bodyMaskTint;
    private static SmartShader bodyMaskHue;
    private static final ArrayList<TextureCombinerShaderParam> bodyMaskParams = new ArrayList();
    private static SmartShader addHole;
    private static final ArrayList<TextureCombinerShaderParam> addHoleParams = new ArrayList();
    private static SmartShader removeHole;
    private static final ArrayList<TextureCombinerShaderParam> removeHoleParams = new ArrayList();
    private static SmartShader blit;

    public SmartTexture() {
        this.name = "SmartTexture";
    }

    void addToCat(int _int) {
        ArrayList arrayList = null;
        if (!this.categoryMap.containsKey(_int)) {
            arrayList = new ArrayList();
            this.categoryMap.put(_int, arrayList);
        } else {
            arrayList = (ArrayList)this.categoryMap.get(_int);
        }

        arrayList.add(this.commands.size());
    }

    public TextureCombinerCommand getFirstFromCategory(int cat) {
        return !this.categoryMap.containsKey(cat) ? null : (TextureCombinerCommand)this.commands.get((Integer)((ArrayList)this.categoryMap.get(cat)).get(0));
    }

    public void addOverlayPatches(String tex, String mask, int category) {
        if (blit == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        this.add(tex, blit, mask, arrayList, 770, 771);
    }

    public void addOverlay(String tex, String mask, float intensity, int category) {
        if (masked == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("intensity", intensity));
        arrayList.add(new TextureCombinerShaderParam("bloodDark", 0.5F, 0.5F));
        this.addSeparate(tex, masked, mask, arrayList, 774, 771, 772, 771);
    }

    public void addDirtOverlay(String tex, String mask, float intensity, int category) {
        if (dirtMask == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("intensity", intensity));
        this.addSeparate(tex, dirtMask, mask, arrayList, 774, 771, 772, 771);
    }

    public void addOverlay(String tex) {
        if (tint == null) {
            this.create();
        }

        this.addSeparate(tex, 774, 771, 772, 771);
    }

    public void addRect(String tex, int x, int y, int w, int h) {
        this.commands.add(TextureCombinerCommand.get().init(getTextureWithFlags(tex), x, y, w, h));
        this.dirty = true;
    }

    @Override
    public void destroy() {
        if (this.result != null) {
            TextureCombiner.instance.releaseTexture(this.result);
        }

        this.clear();
        this.dirty = false;
    }

    public void addTint(String tex, int category, float r, float g, float b) {
        this.addTint(getTextureWithFlags(tex), category, r, g, b);
    }

    public void addTint(Texture tex, int category, float r, float g, float b) {
        if (tint == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("R", r));
        arrayList.add(new TextureCombinerShaderParam("G", g));
        arrayList.add(new TextureCombinerShaderParam("B", b));
        this.add(tex, tint, arrayList);
    }

    public void addHue(String tex, int category, float h) {
        this.addHue(getTextureWithFlags(tex), category, h);
    }

    public void addHue(Texture tex, int category, float h) {
        if (hue == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("HueChange", h));
        this.add(tex, hue, arrayList);
    }

    public Texture addHole(BloodBodyPartType part) {
        String string = "media/textures/HoleTextures/" + CharacterSmartTexture.MaskFiles[part.index()] + ".png";
        if (addHole == null) {
            this.create();
        }

        this.addToCat(CharacterSmartTexture.ClothingItemCategory);
        this.calculate();
        Texture texture = this.result;
        this.clear();
        this.result = null;
        this.commands.add(TextureCombinerCommand.get().initSeparate(texture, addHole, addHoleParams, getTextureWithFlags(string), 770, 0, 1, 771));
        this.dirty = true;
        return texture;
    }

    public void removeHole(String bodyTex, BloodBodyPartType part) {
        String string = "media/textures/HoleTextures/" + CharacterSmartTexture.MaskFiles[part.index()] + ".png";
        this.removeHole(getTextureWithFlags(bodyTex), getTextureWithFlags(string), part);
    }

    public void removeHole(Texture bodyTex, BloodBodyPartType part) {
        String string = "media/textures/HoleTextures/" + CharacterSmartTexture.MaskFiles[part.index()] + ".png";
        this.removeHole(bodyTex, getTextureWithFlags(string), part);
    }

    public void removeHole(Texture bodyTex, Texture maskTex, BloodBodyPartType part) {
        if (removeHole == null) {
            this.create();
        }

        this.addToCat(CharacterSmartTexture.ClothingItemCategory);
        this.commands.add(TextureCombinerCommand.get().init(bodyTex, removeHole, removeHoleParams, maskTex, 770, 771));
        this.dirty = true;
    }

    public void mask(String tex, String maskTex, int category) {
        this.mask(getTextureWithFlags(tex), getTextureWithFlags(maskTex), category);
    }

    public void mask(Texture tex, Texture maskTex, int category) {
        if (bodyMask == null) {
            this.create();
        }

        this.addToCat(category);
        this.commands.add(TextureCombinerCommand.get().init(tex, bodyMask, bodyMaskParams, maskTex, 770, 771));
        this.dirty = true;
    }

    public void maskHue(String tex, String maskTex, int category, float h) {
        this.maskHue(getTextureWithFlags(tex), getTextureWithFlags(maskTex), category, h);
    }

    public void maskHue(Texture tex, Texture maskTex, int category, float h) {
        if (bodyMask == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("HueChange", h));
        this.commands.add(TextureCombinerCommand.get().init(tex, bodyMaskHue, arrayList, maskTex, 770, 771));
        this.dirty = true;
    }

    public void maskTint(String tex, String maskTex, int category, float r, float g, float b) {
        this.maskTint(getTextureWithFlags(tex), getTextureWithFlags(maskTex), category, r, g, b);
    }

    public void maskTint(Texture tex, Texture maskTex, int category, float r, float g, float b) {
        if (bodyMask == null) {
            this.create();
        }

        this.addToCat(category);
        ArrayList arrayList = new ArrayList();
        arrayList.add(new TextureCombinerShaderParam("R", r));
        arrayList.add(new TextureCombinerShaderParam("G", g));
        arrayList.add(new TextureCombinerShaderParam("B", b));
        this.commands.add(TextureCombinerCommand.get().init(tex, bodyMaskTint, arrayList, maskTex, 770, 771));
        this.dirty = true;
    }

    public void addMaskedTexture(CharacterMask mask, String masksFolder, String base, int category, ImmutableColor _tint, float _hue) {
        addMaskedTexture(this, mask, masksFolder, getTextureWithFlags(base), category, _tint, _hue);
    }

    public void addMaskedTexture(CharacterMask mask, String masksFolder, Texture base, int category, ImmutableColor _tint, float _hue) {
        addMaskedTexture(this, mask, masksFolder, base, category, _tint, _hue);
    }

    private static void addMaskFlags(SmartTexture smartTexture, CharacterMask characterMask, String string, Texture texture, int _int) {
        Consumer consumer = Lambda.consumer(
            smartTexture,
            string,
            texture,
            _int,
            (part, smartTexturex, stringx, texturex, integer) -> smartTexturex.mask(texturex, getTextureWithFlags(stringx + "/" + part + ".png"), integer)
        );
        characterMask.forEachVisible(consumer);
    }

    private static void addMaskFlagsHue(SmartTexture smartTexture, CharacterMask characterMask, String string, Texture texture, int _int, float _float) {
        Consumer consumer = Lambda.consumer(
            smartTexture,
            string,
            texture,
            _int,
            _float,
            (part, smartTexturex, stringx, texturex, integer, _floatx) -> smartTexturex.maskHue(
                    texturex, getTextureWithFlags(stringx + "/" + part + ".png"), integer, _floatx
                )
        );
        characterMask.forEachVisible(consumer);
    }

    private static void addMaskFlagsTint(
        SmartTexture smartTexture, CharacterMask characterMask, String string, Texture texture, int _int, ImmutableColor immutableColor
    ) {
        Consumer consumer = Lambda.consumer(
            smartTexture,
            string,
            texture,
            _int,
            immutableColor,
            (part, smartTexturex, stringx, texturex, integer, immutableColorx) -> smartTexturex.maskTint(
                    texturex, getTextureWithFlags(stringx + "/" + part + ".png"), integer, immutableColorx.r, immutableColorx.g, immutableColorx.b
                )
        );
        characterMask.forEachVisible(consumer);
    }

    private static void addMaskedTexture(
        SmartTexture smartTexture, CharacterMask characterMask, String string, Texture texture, int _int, ImmutableColor immutableColor, float _float
    ) {
        if (!characterMask.isNothingVisible()) {
            if (characterMask.isAllVisible()) {
                if (!ImmutableColor.white.equals(immutableColor)) {
                    smartTexture.addTint(texture, _int, immutableColor.r, immutableColor.g, immutableColor.b);
                } else if (!(_float < -1.0E-4F) && !(_float > 1.0E-4F)) {
                    smartTexture.add(texture);
                } else {
                    smartTexture.addHue(texture, _int, _float);
                }
            } else {
                if (!ImmutableColor.white.equals(immutableColor)) {
                    addMaskFlagsTint(smartTexture, characterMask, string, texture, _int, immutableColor);
                } else if (!(_float < -1.0E-4F) && !(_float > 1.0E-4F)) {
                    addMaskFlags(smartTexture, characterMask, string, texture, _int);
                } else {
                    addMaskFlagsHue(smartTexture, characterMask, string, texture, _int, _float);
                }
            }
        }
    }

    public void addTexture(String base, int category, ImmutableColor _tint, float _hue) {
        addTexture(this, base, category, _tint, _hue);
    }

    private static void addTexture(SmartTexture smartTexture, String string, int _int, ImmutableColor immutableColor, float _float) {
        if (!ImmutableColor.white.equals(immutableColor)) {
            smartTexture.addTint(string, _int, immutableColor.r, immutableColor.g, immutableColor.b);
        } else if (!(_float < -1.0E-4F) && !(_float > 1.0E-4F)) {
            smartTexture.add(string);
        } else {
            smartTexture.addHue(string, _int, _float);
        }
    }

    private void create() {
        tint = new SmartShader("hueChange");
        hue = new SmartShader("hueChange");
        masked = new SmartShader("overlayMask");
        dirtMask = new SmartShader("dirtMask");
        bodyMask = new SmartShader("bodyMask");
        bodyMaskHue = new SmartShader("bodyMaskHue");
        bodyMaskTint = new SmartShader("bodyMaskTint");
        addHole = new SmartShader("addHole");
        removeHole = new SmartShader("removeHole");
        blit = new SmartShader("blit");
    }

    @Override
    public WrappedBuffer getData() {
        synchronized (this) {
            if (this.dirty) {
                this.calculate();
            }

            return this.result.dataid.getData();
        }
    }

    @Override
    public synchronized void bind() {
        if (this.dirty) {
            this.calculate();
        }

        this.result.bind(3553);
    }

    @Override
    public int getID() {
        synchronized (this) {
            if (this.dirty) {
                this.calculate();
            }
        }

        return this.result.dataid.id;
    }

    public void calculate() {
        synchronized (this) {
            if (Core.bDebug) {
                GL11.glGetError();
            }

            try {
                this.result = TextureCombiner.instance.combine(this.commands);
            } catch (Exception exception) {
                DebugLog.General.error(exception.getClass().getSimpleName() + " encountered while combining texture.");
                DebugLog.General.error("Intended width : " + TextureCombiner.getResultingWidth(this.commands));
                DebugLog.General.error("Intended height: " + TextureCombiner.getResultingHeight(this.commands));
                DebugLog.General.error("");
                DebugLog.General.error("Commands list: " + PZArrayUtil.arrayToString(this.commands));
                DebugLog.General.error("");
                DebugLog.General.error("Stack trace: ");
                ExceptionLogger.logException(exception);
                DebugLog.General.error("This SmartTexture will no longer be valid.");
                this.width = -1;
                this.height = -1;
                this.dirty = false;
                return;
            }

            this.width = this.result.width;
            this.height = this.result.height;
            this.dirty = false;
        }
    }

    public void clear() {
        TextureCombinerCommand.pool.release(this.commands);
        this.commands.clear();
        this.categoryMap.clear();
        this.dirty = false;
    }

    public void add(String tex) {
        this.add(getTextureWithFlags(tex));
    }

    public void add(Texture tex) {
        if (blit == null) {
            this.create();
        }

        this.commands.add(TextureCombinerCommand.get().init(tex, blit));
        this.dirty = true;
    }

    public void add(String tex, SmartShader shader, ArrayList<TextureCombinerShaderParam> params) {
        this.add(getTextureWithFlags(tex), shader, params);
    }

    public void add(Texture tex, SmartShader shader, ArrayList<TextureCombinerShaderParam> params) {
        this.commands.add(TextureCombinerCommand.get().init(tex, shader, params));
        this.dirty = true;
    }

    public void add(String tex, SmartShader shader, String maskTex, int srcBlend, int destBlend) {
        this.add(getTextureWithFlags(tex), shader, getTextureWithFlags(maskTex), srcBlend, destBlend);
    }

    public void add(Texture tex, SmartShader shader, Texture maskTex, int srcBlend, int destBlend) {
        this.commands.add(TextureCombinerCommand.get().init(tex, shader, maskTex, srcBlend, destBlend));
        this.dirty = true;
    }

    public void add(String tex, int srcBlend, int destBlend) {
        this.add(getTextureWithFlags(tex), srcBlend, destBlend);
    }

    public void add(Texture tex, int srcBlend, int destBlend) {
        this.addSeparate(tex, srcBlend, destBlend, 1, 771);
    }

    public void addSeparate(String tex, int srcBlend, int destBlend, int srcBlendA, int destBlendA) {
        this.addSeparate(getTextureWithFlags(tex), srcBlend, destBlend, srcBlendA, destBlendA);
    }

    public void addSeparate(Texture tex, int srcBlend, int destBlend, int srcBlendA, int destBlendA) {
        this.commands.add(TextureCombinerCommand.get().initSeparate(tex, srcBlend, destBlend, srcBlendA, destBlendA));
        this.dirty = true;
    }

    public void add(String tex, SmartShader shader, String maskTex, ArrayList<TextureCombinerShaderParam> params, int srcBlend, int destBlend) {
        this.add(getTextureWithFlags(tex), shader, getTextureWithFlags(maskTex), params, srcBlend, destBlend);
    }

    public void add(Texture tex, SmartShader shader, Texture maskTex, ArrayList<TextureCombinerShaderParam> params, int srcBlend, int destBlend) {
        this.addSeparate(tex, shader, maskTex, params, srcBlend, destBlend, 1, 771);
    }

    public void addSeparate(
        String tex,
        SmartShader shader,
        String maskTex,
        ArrayList<TextureCombinerShaderParam> params,
        int srcBlend,
        int destBlend,
        int srcBlendA,
        int destBlendA
    ) {
        this.addSeparate(getTextureWithFlags(tex), shader, getTextureWithFlags(maskTex), params, srcBlend, destBlend, srcBlendA, destBlendA);
    }

    public void addSeparate(
        Texture tex,
        SmartShader shader,
        Texture maskTex,
        ArrayList<TextureCombinerShaderParam> params,
        int srcBlend,
        int destBlend,
        int srcBlendA,
        int destBlendA
    ) {
        this.commands.add(TextureCombinerCommand.get().initSeparate(tex, shader, params, maskTex, srcBlend, destBlend, srcBlendA, destBlendA));
        this.dirty = true;
    }

    private static Texture getTextureWithFlags(String string) {
        return Texture.getSharedTexture(string, ModelManager.instance.getTextureFlags());
    }

    @Override
    public void saveOnRenderThread(String filename) {
        if (this.dirty) {
            this.calculate();
        }

        this.result.saveOnRenderThread(filename);
    }

    protected void setDirty() {
        this.dirty = true;
    }

    @Override
    public boolean isEmpty() {
        return this.result == null ? true : this.result.isEmpty();
    }

    @Override
    public boolean isFailure() {
        return this.result == null ? false : this.result.isFailure();
    }

    @Override
    public boolean isReady() {
        return this.result == null ? false : this.result.isReady();
    }
}
